module common.particle;

import std.math, std.stdio, std.random, std.string;

import mirshare;
import common.imagearray;
import share.types;

import allegro5.allegro;
import allegro5.allegro_primitives;
import allegro5.transformations;
import allegro5.bitmap_io;

public float RandomFloat()
{
    return uniform(0.0f, 1.0f);
}

public struct Particle
{
    // ÀÔÀÚ°ü·Ã Á¤º¸.
    public ushort           m_wLife;                    // ¼ö¸í.
    public ushort           m_wCurrLife;                // ÇöÀç¼ö¸í.
    public FloatPoint       m_vecPos;                   // ÁÂÇ¥.
    public FloatPoint       m_vecOldPos;                // ÁÂÇ¥ º¸°ü.
    public FloatPoint       m_vecVel;                   // ¼Óµµ.
    public FloatPoint       m_vecAccel;                 // °¡¼Óµµ.
    public FloatPoint       m_vecLocalForce;            // ÆÄÆ¼Å¬¿¡ °³º° ¿µÇâÀ» ÁÖ´Â Èû.
    public float            m_fMass;                    // Áú·®.
    public float            m_fSize;                    // Å©±â.
    public bool             m_bIsDead;                  // ÆÄÆ¼Å¬ÀÇ Á¸Àç¿©ºÎ.
    public ubyte            m_bRed, m_bGreen, m_bBlue;  // »ö.
    public ubyte            m_bOpa;
    
    public float            m_fOriSize;                 // Ã³À½Å©±â.
    public ubyte            m_bFstRed, m_bFstGreen, m_bFstBlue; // ÆÄÆ¼Å¬ »ý¼º½ÃÁ¡ÀÇ»ö.
    public ubyte            m_bEndRed, m_bEndGreen, m_bEndBlue; // ÆÄÆ¼Å¬ Á¾·á½ÃÁ¡ÀÇ»ö.
    
    //------------------------------------------------------------------------------------------------------------------
    // ÀÔÀÚ Frame°ü·Ã Á¤º¸.
    public ushort           m_wDelay;                   // Animation Delay.
    public ushort           m_wCurrDelay;
    public ushort           m_wCurrFrame;               


    private static Particle makeZero()
    {
        Particle pt = void;
        pt.m_wLife = 0;    
        pt.m_wCurrLife = 0;
        
        pt.m_vecPos = FloatPoint.zero;     
        pt.m_vecOldPos = FloatPoint.zero;     
        pt.m_vecVel = FloatPoint.zero;     
        pt.m_vecAccel = FloatPoint.zero;     
        pt.m_vecLocalForce = FloatPoint.zero;
        
        pt.m_fMass = 0.0f;
        pt.m_fSize = 0.0f; 
        pt.m_fOriSize = 0.0f; 
        pt.m_bIsDead = true;     
        
        pt.m_bRed = pt.m_bGreen = pt.m_bBlue = 255;
        pt.m_bOpa = 255;
        pt.m_bFstRed = pt.m_bFstGreen = pt.m_bFstBlue = 255;
        pt.m_bEndRed = pt.m_bEndGreen = pt.m_bEndBlue = 255;
        
        pt.m_wDelay = 0;        
        pt.m_wCurrDelay = 0;
        pt.m_wCurrFrame = 0;

        return pt;
    }

    public static immutable Particle zero = makeZero();
}



public abstract class ParticleSystem
{
    protected FloatPoint[2] m_vecBounding;      // Ãæµ¹Ã¼Å©¿µ¿ª ¼³Á¤.
    
    protected int           m_wNum;                 // ÀÔÀÚÀÇ °¹¼ö.
    protected Particle[]    m_pxParticle;           // ÀÔÀÚ °³Ã¼.
    protected ALLEGRO_VERTEX[4] m_avShape;          // Vertex.
    
    protected float         m_fDeltaTime;
    
    protected FloatPoint    m_vecEnvironment;       // ÀÔÀÚ¿¡ ÀÛ¿ëÇÏ´Â È¯°æº¯¼ö(Áß·Â, ¹Ù¶÷).
    public float            m_fAirFiction;          // °ø±â¸¶Âû°³¼ö.(¹Ù¶÷.)

    private ImageArray      m_images;
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }

    public void SetImages(ImageArray iarr)
    {
        m_images = iarr;
    }
    
    // °ø±â¸¶Âû°è¼ö Àû¿ë.
    public void UpdateAirFiction(int index)
    {
        auto pt = &m_pxParticle[index];
        if ( !pt.m_bIsDead )
        {
            pt.m_vecLocalForce.setX( -pt.m_vecVel.getX() * m_fAirFiction );
            pt.m_vecLocalForce.setY( -pt.m_vecVel.getY() * m_fAirFiction );
        }
    }
    
    // Áß·ÂÀå Àû¿ë.
    public void UpdateMove(int index)
    {
        auto pt = &m_pxParticle[index];
        
        if ( !pt.m_bIsDead )
        {
            pt.m_vecAccel.setX( pt.m_vecAccel.getX() + ((m_vecEnvironment.getX() + pt.m_vecLocalForce.getX()) / pt.m_fMass) );
            pt.m_vecAccel.setY( pt.m_vecAccel.getY() + ((m_vecEnvironment.getY() + pt.m_vecLocalForce.getY()) / pt.m_fMass) );
            
            pt.m_vecVel.setX( pt.m_vecVel.getX() + (pt.m_vecAccel.getX() * m_fDeltaTime) );//*0.01f;
            pt.m_vecVel.setY( pt.m_vecVel.getY() + (pt.m_vecAccel.getY() * m_fDeltaTime) );//*0.01f;
            
            pt.m_vecOldPos = pt.m_vecPos;
            
            pt.m_vecPos.setX( pt.m_vecPos.getX() + (pt.m_vecVel.getX() * m_fDeltaTime) );//*0.01f;
            pt.m_vecPos.setY( pt.m_vecPos.getY() + (pt.m_vecVel.getY() * m_fDeltaTime) );//*0.01f;
        }
    }
    
    // Ãæµ¹¹Ý¹ß·Â Àû¿ë.
    public void UpdateCrash(ushort index)
    {
        auto pt = &m_pxParticle[index];
        
        if ( !pt.m_bIsDead )
        {
            if ( pt.m_vecPos.getX() <= m_vecBounding[0].getX() || pt.m_vecPos.getX() >= m_vecBounding[1].getX() )
            {   
                pt.m_vecVel.setX( -pt.m_vecVel.getX() * 0.7f );
            }
            
            if ( pt.m_vecPos.getY() <= m_vecBounding[0].getY() || pt.m_vecPos.getY() >= m_vecBounding[1].getY() )
            {
                auto fOldX = pt.m_vecPos.getX() - pt.m_vecVel.getX() * pt.m_wDelay;
                auto fOldY = pt.m_vecPos.getY() - pt.m_vecVel.getY() * pt.m_wDelay;
                
                auto fBefore = pt.m_wDelay * (m_vecBounding[1].getY() - fOldY)/(pt.m_vecPos.getY() - fOldY);                         // Ãæµ¹Àü.
                auto fAfter  = pt.m_wDelay * (pt.m_vecPos.getY() - m_vecBounding[1].getY())/(pt.m_vecPos.getY() - fOldY); // Ãæµ¹ÈÄ.
                
                pt.m_vecPos.setX( fOldX + pt.m_vecVel.getX() * fBefore );
                pt.m_vecPos.setY( fOldY + pt.m_vecVel.getY() * fBefore );
                
                pt.m_vecVel.setY( -pt.m_vecVel.getY() * 0.6f );
                
                pt.m_vecPos.setX( pt.m_vecPos.getX() + (pt.m_vecVel.getX() * fAfter) );
                pt.m_vecPos.setY( pt.m_vecPos.getY() + (pt.m_vecVel.getY() * fAfter) );            
            }
        }
    }
    
    public void SetEnvFactor(float fAirFriction = -0.05f, 
                             FloatPoint vecEnvironment = FloatPoint(0, 300), 
                             FloatPoint vecMinBound = FloatPoint.zero, 
                             FloatPoint vecMaxBound = FloatPoint.zero)
    {
        m_fAirFiction    = fAirFriction;
        m_vecEnvironment = vecEnvironment;
        m_vecBounding[0] = vecMinBound;
        m_vecBounding[1] = vecMaxBound;
    }
    
    public void InitSystem()
    {
        m_wNum           = 0;       
        m_pxParticle     = null;
        m_fDeltaTime     = 0.02f;
        
        m_fAirFiction    = -0.05f;
        m_vecBounding[0] = FloatPoint.zero;
        m_vecBounding[1] = FloatPoint.zero;
        m_vecEnvironment = FloatPoint(0, 300);
        
        // D3DVECTOR vNorm(0, 0, -1);
        ALLEGRO_COLOR color = al_map_rgba(0, 0, 0, 255);
        m_avShape[0] = ALLEGRO_VERTEX(-0.5f,-0.5f, 0.0f, 0.0f, 1.0f, color);
        m_avShape[1] = ALLEGRO_VERTEX( 0.5f,-0.5f, 0.0f, 0.0f, 0.0f, color);
        m_avShape[2] = ALLEGRO_VERTEX(-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, color);
        m_avShape[3] = ALLEGRO_VERTEX( 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, color);
    }
    
    public void SetupSystem(int count = 200)
    {
        InitSystem();
        m_wNum = count;
        m_pxParticle = new Particle[m_wNum];
        
        foreach (int index; 0..m_wNum)
        {
            m_pxParticle[index] = Particle.zero;
        }
    }
    
    public void DestroySystem()
    {
        m_pxParticle = null;
        InitSystem();
    }
    
    public abstract void LoadTextr();
    public abstract bool RenderSystem();
    public abstract void UpdateSystem(int nLoopTime, FloatPoint vecGenPos);
    public abstract void SetParticleDefault(int index, FloatPoint vecGenPos);
}



const _MAX_SNOWFRAME = 1;

public class SnowSystem : ParticleSystem
{
    private ALLEGRO_BITMAP*[_MAX_SNOWFRAME] m_szSnowBmp;
    private float               m_fWidth, m_fGround;
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }
    
    public override void LoadTextr()
    {
        static immutable string[] szFileList = 
        [
            "snow0.bmp",
        ];

        foreach (int nCnt; 0.._MAX_SNOWFRAME)
        {
            m_szSnowBmp[nCnt] = al_load_bitmap(szFileList[nCnt].toStringz);
        }
    }
    
    public override void InitSystem()
    {
        super.InitSystem();
        m_fWidth = m_fGround = 0.0f;
        m_szSnowBmp[] = null;
    }
    
    public override void DestroySystem()
    {
        super.DestroySystem();

        foreach (int nCnt; 0.._MAX_SNOWFRAME)
        {
            al_destroy_bitmap(m_szSnowBmp[nCnt]);
        }
        
        InitSystem();
    }
    
    public void SetupSnowSystem(int count = 200, float fWidth = 800f, float fGround = 600f)
    {
        InitSystem();
        super.SetupSystem(count);
        
        m_fWidth  = fWidth;
        m_fGround = fGround;
        LoadTextr();
        
        SetEnvFactor(-0.05f, FloatPoint(10, 100));
    }
    
    public override bool RenderSystem()
    {
        int op, source, dest;
        al_get_blender(&op, &source, &dest);
        al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_INVERSE_ALPHA);

        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];

            if ( pt.m_bIsDead )
                continue;

            immutable tint_color = al_map_rgba(pt.m_bRed, pt.m_bGreen, pt.m_bBlue, 0);

            al_draw_tinted_scaled_rotated_bitmap(m_szSnowBmp[pt.m_wCurrFrame],
                                                 tint_color, 0f, 0f, pt.m_vecPos.getX(), pt.m_vecPos.getY(), pt.m_fSize, pt.m_fSize,
                                                 0f, 0);
        }
        
        al_set_blender(op, source, dest);
        
        return true;
    }
    
    public override void UpdateSystem(int nLoopTime, FloatPoint vecGenPos = FloatPoint.zero)
    {
        auto nGenCnt = 0; 
        auto nSpeedRate = (nLoopTime / 17);
        
        if ( nSpeedRate < 1 )       
            nSpeedRate = 1;
        
        m_fDeltaTime = 0.02f * nSpeedRate;
        
        foreach (int index; 0..m_wNum)
        {
            auto pt = &m_pxParticle[index];
            
            pt.m_wCurrLife += nLoopTime;
            
            if ( pt.m_wCurrLife > pt.m_wLife || pt.m_vecPos.getY() >= m_fGround )
            {
                if( (pt.m_wCurrLife-pt.m_wLife) > 255 )
                {
                    pt.m_bIsDead = true;
                    pt.m_wCurrLife = 0;
                }
                else
                {
                    auto bRate = cast(ubyte)(255 - (pt.m_wCurrLife-pt.m_wLife));
                    pt.m_bOpa = bRate;
                    
                    if ( bRate < pt.m_bRed ) pt.m_bRed = bRate;
                    if ( bRate < pt.m_bGreen ) pt.m_bGreen = bRate;
                    if ( bRate < pt.m_bBlue ) pt.m_bBlue = bRate;
                    
                    continue;
                }
            }
            
            if ( pt.m_bIsDead && nGenCnt < 20 )
            {
                SetParticleDefault(index);
                nGenCnt++;
            }
            else
            {
                UpdateAirFiction(index);
                UpdateMove(index);
                
                pt.m_wCurrDelay += nLoopTime;
                if ( pt.m_wCurrDelay > pt.m_wDelay )
                {
                    pt.m_wCurrDelay = 0;
                    pt.m_wCurrFrame++;
                    
                    //float fRand = _RAND_FLOAT;
                    //SetEnvFactor(fRand, Vector2(-fRand, fRand*100, 0));
                    
                    if ( pt.m_wCurrFrame >= _MAX_SNOWFRAME )
                    {
                        pt.m_wCurrFrame = 0;
                    }
                }
            }
        }
    }
    
    public override void SetParticleDefault(int index, FloatPoint vecGenPos = FloatPoint.zero)
    {
        auto pt = &m_pxParticle[index];

        *pt = Particle.zero;
        pt.m_vecPos     = FloatPoint(uniform(0f, m_fWidth)/*(-400, 1200)*/, uniform(-300f,  0f));
        pt.m_vecVel     = FloatPoint(uniform(-30f,  30f), uniform(70f, 100f));

        pt.m_wLife      = cast(ushort)uniform(2500,  7000);
        pt.m_fMass      = 1000.0f + RandomFloat();
        pt.m_fSize      = uniform(0.1f, 0.4f);
        pt.m_bRed       = pt.m_bGreen = pt.m_bBlue = cast(ubyte)uniform(150, 200);
        pt.m_bBlue      += 50;
        
        pt.m_wDelay     = 300;
        //pt.m_wDelay     = cast(ushort)uniform(150, 300);
        
        pt.m_bIsDead    = false;    
    }
}



const _MAX_RAINFRAME = 1;
const _MAX_RAINDROPFRAME = 5;

public class RainSystem : ParticleSystem
{
    private ALLEGRO_BITMAP*[_MAX_RAINFRAME] m_pszRainFile;  
    private ALLEGRO_BITMAP*[_MAX_RAINDROPFRAME] m_pszRainDropFile;
    
    private float m_fWidth, m_fGround;
    
    private int m_bRainIntensity;         // ºñÀÇ°­µµ.
    
    
    public int m_bGenCnt;                 
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }
    
    public override void LoadTextr()
    {
        static immutable string[] szFileList1 = 
        [
            "rain0.bmp",
        ];
        static immutable string[] szFileList2 = 
        [
            "raindrop0.bmp",
            "raindrop1.bmp",
            "raindrop2.bmp",
            "raindrop3.bmp",
            "raindrop4.bmp",
        ];
        
        for ( int nCnt = 0; nCnt < _MAX_RAINFRAME; nCnt++ )
        {
            m_pszRainFile[nCnt] = al_load_bitmap(szFileList1[nCnt].toStringz);
        }
        for ( int nCnt = 0; nCnt < _MAX_RAINDROPFRAME; nCnt++ )
        {
            m_pszRainDropFile[nCnt] = al_load_bitmap(szFileList2[nCnt].toStringz);
        }
    }
    
    public override void InitSystem()
    {
        super.InitSystem();
        m_fWidth = m_fGround = 0.0f;
        m_bRainIntensity = 0;
        m_bGenCnt = 10;
        m_pszRainFile[] = null;
        m_pszRainDropFile[] = null;
        //      ZeroMemory(m_pszRainFile, _MAX_RAINFRAME*MAX_PATH);
        //      ZeroMemory(m_pszRainDropFile, _MAX_RAINDROPFRAME*MAX_PATH);
    }
    
    
    public override void DestroySystem()
    {
        super.DestroySystem();
        
        for ( int nCnt = 0; nCnt < _MAX_RAINFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_pszRainFile[nCnt]);
        }
        for ( int nCnt = 0; nCnt < _MAX_RAINDROPFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_pszRainDropFile[nCnt]);
        }
        InitSystem();
    }
    
    public void SetupRainSystem(int count = 400, float fWidth = 800f, float fGround = 600f)
    {
        InitSystem();
        super.SetupSystem(count);
        
        m_fWidth  = fWidth;
        m_fGround = fGround;
        LoadTextr();
        
        SetEnvFactor(-0.05f, FloatPoint(10, 100));
    }
    
    public override bool RenderSystem()
    {
        int op, source, dest;
        al_get_blender(&op, &source, &dest);
        al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_INVERSE_ALPHA);
        
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
                continue;
            
            immutable tint_color = al_map_rgba(pt.m_bRed, pt.m_bGreen, pt.m_bBlue, 0);
            
            immutable angle = std.math.PI_2 - std.math.atan((pt.m_vecPos.getY() - pt.m_vecOldPos.getY())/(pt.m_vecPos.getX() - pt.m_vecOldPos.getX()));

            // PDS:
            /*al_draw_tinted_scaled_rotated_bitmap(m_pszRainDropFile[pt.m_wCurrFrame],
                                                 tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), 0.3f, pt.m_fSize,
                                                 angle, 0);*/

            int life = pt.m_wCurrLife - pt.m_wLife;
            if ( life > 0 && life < 510 )
            {
                const float scale = 64.0f / 100.0f;

                al_draw_tinted_scaled_rotated_bitmap(m_pszRainFile[0/*pt.m_wCurrFrame*/],
                                                     tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), scale, scale,
                                                     angle, 0);
            }
            else
            {
                al_draw_tinted_scaled_rotated_bitmap(m_pszRainDropFile[pt.m_wCurrFrame],
                                                     tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), 0.3f, pt.m_fSize,
                                                     angle, 0);
            }
        }
        
        al_set_blender(op, source, dest);
        
        return true;
    }
    
    public override void UpdateSystem(int nLoopTime, FloatPoint vecGenPos = FloatPoint.zero)
    {
        int nGenCnt = 0; 
        int nSpeedRate = (nLoopTime / 17);
        if ( nSpeedRate < 1 )       
            nSpeedRate = 1;
        
        m_fDeltaTime = 0.02f*nSpeedRate;
        
        foreach (int index; 0..m_wNum)
        {
            auto pt = &m_pxParticle[index];
            
            pt.m_wCurrLife += nLoopTime;
            
            if ( pt.m_wCurrLife > pt.m_wLife || pt.m_vecPos.getY() >= m_fGround )
            {
                if ( (pt.m_wCurrLife-pt.m_wLife) > /*_MAX_RAINDROPFRAME*50*/250 )
                {
                    pt.m_bIsDead = true;
                }
                else
                {
                    ubyte bRate =  cast(ubyte)(/*50*_MAX_RAINDROPFRAME*/250 - (pt.m_wCurrLife-pt.m_wLife) / 4);
                    
                    pt.m_bOpa = bRate;
                    pt.m_wDelay = 50;
                    
                    if ( bRate < pt.m_bRed ) pt.m_bRed = bRate;
                    if ( bRate < pt.m_bGreen ) pt.m_bGreen = bRate;
                    if ( bRate < pt.m_bBlue ) pt.m_bBlue = bRate;
                    
                    pt.m_wCurrDelay += nLoopTime;
                    if ( pt.m_wCurrDelay > pt.m_wDelay )
                    {
                        pt.m_wCurrDelay = 0;
                        pt.m_wCurrFrame++;
                        
                        if ( pt.m_wCurrFrame >= _MAX_RAINDROPFRAME )
                        {
                            pt.m_wCurrFrame = 0;
                        }
                    }
                    
                    continue;
                }
            }
            
            if ( pt.m_bIsDead && nGenCnt < m_bGenCnt )
            {
                SetParticleDefault(index);
                nGenCnt++;
            }
            else
            {
                UpdateAirFiction(index);
                UpdateMove(index);
                
                pt.m_wCurrDelay += nLoopTime;
                if ( pt.m_wCurrDelay > pt.m_wDelay )
                {
                    pt.m_wCurrDelay = 0;
                    pt.m_wCurrFrame++;
                    
                    if ( pt.m_wCurrFrame >= _MAX_RAINFRAME )
                    {
                        pt.m_wCurrFrame = 0;
                    }
                }
            }
        }
    }
    
    public override void SetParticleDefault(int index, FloatPoint vecGenPos = FloatPoint.zero)
    {
        auto pt = &m_pxParticle[index];
        
        *pt = Particle.zero;
        pt.m_vecPos     = FloatPoint(uniform(-100, m_fWidth+100)/*(-400, 1200)*/, uniform(-500f,  0f));
        pt.m_vecVel     = FloatPoint(0.0f, uniform(450f, 600f));
        //pt.m_vecAccel   = FloatPoint.Zero;
        //pt.m_vecOldPos  = FloatPoint.Zero;
        //pt.m_vecLocalForce = FloatPoint.Zero;
        pt.m_wLife      = cast(ushort)uniform(800, 1400);

        pt.m_fMass      = 100.0f;
        pt.m_fSize      = uniform(0.3f, 3.0f);
        pt.m_bRed       = pt.m_bGreen = pt.m_bBlue = cast(ubyte)uniform(120, 180);
        pt.m_bBlue      = 125;
        
        pt.m_wDelay     = cast(ushort)uniform(50, 150);
        //pt.m_wCurrLife  = 0;
        //pt.m_wCurrDelay = 0;
        //pt.m_wCurrFrame = 0;
        //pt.m_bOpa       = 255;
        
        pt.m_bIsDead    = false;    
    }
}



const _MAX_FLYFRAME = 10;

public class Fly : ParticleSystem
{
    private ALLEGRO_BITMAP*[_MAX_FLYFRAME] m_pszFlyFile;
    private ubyte m_bGenCnt;
    
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }
    
     
    public override void LoadTextr()
    {
        static immutable string[] szFileList = 
        [
            "shine0.bmp",
            "shine1.bmp",
            "shine2.bmp",
            "shine3.bmp",
            "shine4.bmp",
            "shine5.bmp",
            "shine6.bmp",
            "shine7.bmp",
            "shine8.bmp",
            "shine9.bmp"
        ];
        for ( int nCnt = 0; nCnt < _MAX_FLYFRAME; nCnt++ )
        {
            m_pszFlyFile[nCnt] = al_load_bitmap(szFileList[nCnt].toStringz);
        }
    }

    public override void InitSystem()
    {
        super.InitSystem();
        m_bGenCnt = 3;
        m_pszFlyFile[] = null;
    }

    public override void DestroySystem()
    {
        super.DestroySystem();
        
        for ( int nCnt = 0; nCnt < _MAX_FLYFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_pszFlyFile[nCnt]);
        }
        InitSystem();
    }

    public override void SetupSystem(int count = 100)
    {
        InitSystem();
        super.SetupSystem(count);
        
        LoadTextr();
        
        SetEnvFactor(-0.05f, FloatPoint(100, 1000));
    }

    public override bool RenderSystem()
    {
        ALLEGRO_TRANSFORM identity, trans, scale, world;
        
        al_identity_transform(&identity);
        al_identity_transform(&world);

        int op, source, dest;
        al_get_blender(&op, &source, &dest);
        al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_ONE);
        
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
                continue;
            
            immutable tint_color = al_map_rgba(pt.m_bRed, pt.m_bGreen, pt.m_bBlue, 0);

            al_draw_tinted_scaled_rotated_bitmap(m_pszFlyFile[pt.m_wCurrFrame],
                                                 tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), pt.m_fSize, pt.m_fSize,
                                                 0, 0);
        }
        
        al_set_blender(op, source, dest);
        
        return true;
    }

    public override void UpdateSystem(int nLoopTime, FloatPoint vecGenPos)
    {
        ubyte bRate;
        int nGenCnt = 0; 
        
        int nSpeedRate = (nLoopTime / 17);
        if ( nSpeedRate < 1 )       
            nSpeedRate = 1;
        
        m_fDeltaTime = 0.02f*nSpeedRate;

        foreach (int index; 0..m_wNum)
        {
            auto pt = &m_pxParticle[index];

            pt.m_wCurrLife += nLoopTime;
            
            if ( pt.m_wCurrLife > pt.m_wLife )
            {
                pt.m_bIsDead = true;
            }
            else
            {
                pt.m_wCurrDelay += nLoopTime;
                if ( pt.m_wCurrDelay > pt.m_wDelay )
                {
                    pt.m_wCurrDelay = 0;
                    pt.m_wCurrFrame++;
                    
                    pt.m_fSize += 0.005f;
                    
                    
                    if ( pt.m_wCurrFrame >= _MAX_FLYFRAME )
                    {
                        pt.m_wCurrFrame = 0;
                    }
                }

                float fLife = cast(float)(cast(float)pt.m_wCurrLife / cast(float)pt.m_wLife);
                /*
                bRate = 255 - 255 * fLife;
                pt.m_bRed = pt.m_bGreen = pt.m_bBlue = bRate;
                */

                bRate = cast(ubyte)(pt.m_bFstRed - pt.m_bFstRed * fLife);
                pt.m_bRed   = bRate;
                bRate = cast(ubyte)(pt.m_bFstGreen - pt.m_bFstGreen * fLife);
                pt.m_bGreen = bRate;
                bRate = cast(ubyte)(pt.m_bFstBlue - pt.m_bFstBlue * fLife);
                pt.m_bBlue  = bRate;
                
                UpdateAirFiction(index);
                UpdateMove(index);
            }
            
            if ( pt.m_bIsDead )
            {
                if ( nGenCnt < m_bGenCnt )
                {
                    SetParticleDefault(index, vecGenPos);
                    nGenCnt++;
                }
            }
        }
    }

    public override void SetParticleDefault(int index, FloatPoint vecGenPos)
    {
        auto pt = &m_pxParticle[index];
        // m_bRainIntensity ---> ºñÀÇ °­µµ°¡ ½ÉÇØÁú¼ö·Ï gencnt, mass, size, delay, velocityµî¿¡ ¿µÇâÀ» ¹ÌÄ£´Ù.
        
        *pt = Particle.zero;
        pt.m_vecPos     = vecGenPos;
        pt.m_vecVel     = FloatPoint(uniform(-30, 30), uniform(-20, 40));
        pt.m_wLife      = cast(ushort)uniform(500, 1500);
        pt.m_fMass      = 1000.0f;
        pt.m_fSize      = uniform(0.5f, 4.0f) + RandomFloat();
        //  pt.m_wLife      = cast(ushort)uniform(pt.m_fSize*2000, 6000);

        pt.m_bRed       = pt.m_bFstRed   = cast(ubyte)uniform(200, 255);
        pt.m_bGreen     = pt.m_bFstGreen = pt.m_bFstRed / 2;//uniform(0, 125);
        pt.m_bBlue      = pt.m_bFstBlue  = cast(ubyte)uniform(0, 30);
        pt.m_wDelay     = cast(ushort)uniform(200, 300);

        pt.m_bIsDead    = false;
    }
}



const _MAX_BOOMFRAME = 10;

public class Boom : ParticleSystem
{
    private ALLEGRO_BITMAP*[_MAX_BOOMFRAME] m_pszBoomFile;
    private int m_shPartNum;
    
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }

    public override void LoadTextr()
    {
        static immutable string[] szFileList = 
        [
            "shine0.bmp",
            "shine1.bmp",
            "shine2.bmp",
            "shine3.bmp",
            "shine4.bmp",
            "shine5.bmp",
            "shine6.bmp",
            "shine7.bmp",
            "shine8.bmp",
            "shine9.bmp"
        ];
        for ( int nCnt = 0; nCnt < _MAX_BOOMFRAME; nCnt++ )
        {
            m_pszBoomFile[nCnt] = al_load_bitmap(szFileList[nCnt].toStringz);
        }
    }
    
    public override void InitSystem()
    {
        super.InitSystem();
        m_shPartNum = 0;
        m_pszBoomFile[] = null;
    }
    
    public override void DestroySystem()
    {
        super.DestroySystem();
        
        for ( int nCnt = 0; nCnt < _MAX_BOOMFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_pszBoomFile[nCnt]);
        }
        InitSystem();
    }

    public override void SetupSystem(int count = 1000)
    {
        InitSystem();
        super.SetupSystem(count);
        
        LoadTextr();
        
        SetEnvFactor(-0.05f, FloatPoint(0, 200));
    }

    public override bool RenderSystem()
    {
        int nPartCnt = 0;
        int op, source, dest;
        al_get_blender(&op, &source, &dest);
        al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_INVERSE_ALPHA);
        
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
                continue;
            
            immutable tint_color = al_map_rgba(pt.m_bRed, pt.m_bGreen, pt.m_bBlue, 0);

            al_draw_tinted_scaled_rotated_bitmap(m_pszBoomFile[pt.m_wCurrFrame],
                                                 tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), pt.m_fSize, pt.m_fSize,
                                                 0, 0);
            nPartCnt++;
            if ( nPartCnt >= m_shPartNum )
                break;
        }
        
        al_set_blender(op, source, dest);
        
        return true;
    }

    public void SetBoomParticle(FloatPoint vecGenPos)
    {
        int nGenCnt = 0;
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];

            if ( pt.m_bIsDead )
            {
                SetParticleDefault(index, vecGenPos);
                m_shPartNum++;
                
                nGenCnt++;
                
                if ( nGenCnt > 10 )
                    return;
            }
        }
    }

    public override void UpdateSystem(int nLoopTime, FloatPoint vecGenPos = FloatPoint.zero)
    {
        int nSpeedRate = (nLoopTime / 17);
        int nPartCnt = 0;
        
        if ( nSpeedRate < 1 )       
            nSpeedRate = 1;
        
        m_fDeltaTime = 0.02f*nSpeedRate;
        
        foreach (int index; 0..m_wNum)
        {
            auto pt = &m_pxParticle[index];

            if ( !pt.m_bIsDead )
            {
                pt.m_wCurrLife += nLoopTime;
                
                if ( pt.m_wCurrLife > pt.m_wLife )
                {
                    *pt = Particle.zero;
                    m_shPartNum--;
                    nPartCnt--;
                }
                else
                {
                    pt.m_wCurrDelay += nLoopTime;
                    
                    pt.m_fMass      += 3.0f;
                    
                    if ( pt.m_fSize < 0 )
                        pt.m_fSize = 0;
                    
                    if ( pt.m_wCurrDelay > pt.m_wDelay )
                    {
                        pt.m_wCurrDelay = 0;
                        pt.m_wCurrFrame++;
                        
                        if ( pt.m_wCurrFrame >= _MAX_BOOMFRAME )
                        {
                            pt.m_wCurrFrame = 0;
                        }
                    }

                    float fLife = cast(float)(cast(float)pt.m_wCurrLife / cast(float)pt.m_wLife);
                    pt.m_fSize = (pt.m_fOriSize - pt.m_fOriSize * fLife) / 100;
                    
                    UpdateAirFiction(index);
                    UpdateMove(index);
                }
                
                nPartCnt++;
                if ( nPartCnt >= m_shPartNum )
                    return;
            }
        }
    }

    public override void SetParticleDefault(int index, FloatPoint vecGenPos)
    {
        auto pt = &m_pxParticle[index];

        *pt = Particle.zero;
        pt.m_vecPos     = vecGenPos;
        pt.m_vecVel     = FloatPoint(uniform(-75, 75), uniform(-180, -50));
        pt.m_wLife      = cast(ushort)uniform(800, 1200);
        pt.m_fMass      = 1.0f;
        pt.m_fSize      = pt.m_fOriSize = uniform(0.5f, 4.0f) + RandomFloat();
  
        pt.m_bRed       = pt.m_bFstRed   = cast(ubyte)uniform(0, 255);
        pt.m_bGreen     = pt.m_bFstGreen = pt.m_bFstRed / 2;
        pt.m_bBlue      = pt.m_bFstBlue  = 0;
        pt.m_wDelay     = cast(ushort)uniform(200, 300);

        pt.m_bIsDead    = false;
    }
}



const _MAX_FLYTAILFRAME = 10;

public class FlyingTail : ParticleSystem
{
    private ALLEGRO_BITMAP*[_MAX_FLYTAILFRAME] m_pszFlyingTailFile;
    private int m_shPartNum;
    
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }
    
    public override void LoadTextr()
    {
        static immutable string[] szFileList = 
        [
            "shine0.bmp",
            "shine1.bmp",
            "shine2.bmp",
            "shine3.bmp",
            "shine4.bmp",
            "shine5.bmp",
            "shine6.bmp",
            "shine7.bmp",
            "shine8.bmp",
            "shine9.bmp"
        ];
        for ( int nCnt = 0; nCnt < _MAX_FLYTAILFRAME; nCnt++ )
        {
            m_pszFlyingTailFile[nCnt] = al_load_bitmap(szFileList[nCnt].toStringz);
        }
    }
    
    public override void InitSystem()
    {
        super.InitSystem();
        m_shPartNum = 0;
        m_pszFlyingTailFile[] = null;
    }
    
    public override void DestroySystem()
    {
        super.DestroySystem();
        
        for ( int nCnt = 0; nCnt < _MAX_FLYTAILFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_pszFlyingTailFile[nCnt]);
        }
        InitSystem();
    }
    
    public override void SetupSystem(int count = 500)
    {
        InitSystem();
        super.SetupSystem(count);
        
        LoadTextr();
        
        SetEnvFactor(-0.05f, FloatPoint(100, 1000));
    }
    
    public override bool RenderSystem()
    {
        int nPartCnt = 0;
        int op, source, dest;
        al_get_blender(&op, &source, &dest);
        al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_INVERSE_ALPHA);
        
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
                continue;
            
            immutable tint_color = al_map_rgba(pt.m_bRed, pt.m_bGreen, pt.m_bBlue, 0);
            
            al_draw_tinted_scaled_rotated_bitmap(m_pszFlyingTailFile[pt.m_wCurrFrame],
                                                 tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), pt.m_fSize, pt.m_fSize,
                                                 0, 0);
            nPartCnt++;
            if ( nPartCnt >= m_shPartNum )
                break;
        }
        
        al_set_blender(op, source, dest);
        
        return true;
    }
    
    public void SetFlyTailParticle(FloatPoint vecGenPos)
    {
        int nGenCnt = 0;
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
            {
                SetParticleDefault(index, vecGenPos);
                m_shPartNum++;
                
                nGenCnt++;
                
                if ( nGenCnt > 2 )
                    return;
            }
        }
    }
    
    public override void UpdateSystem(int nLoopTime, FloatPoint vecGenPos = FloatPoint.zero)
    {
        ubyte bRate;
        int nSpeedRate = (nLoopTime / 17);
        int nPartCnt = 0;
        
        if ( nSpeedRate < 1 )       
            nSpeedRate = 1;
        
        m_fDeltaTime = 0.02f*nSpeedRate;
        
        foreach (int index; 0..m_wNum)
        {
            auto pt = &m_pxParticle[index];
            
            if ( !pt.m_bIsDead )
            {
                pt.m_wCurrLife += nLoopTime;
                
                if ( pt.m_wCurrLife > pt.m_wLife )
                {
                    *pt = Particle.zero;
                    m_shPartNum--;
                    nPartCnt--;
                }
                else
                {
                    pt.m_wCurrDelay += nLoopTime;
                    
                    if ( pt.m_wCurrDelay > pt.m_wDelay )
                    {
                        pt.m_wCurrDelay = 0;
                        pt.m_wCurrFrame++;
                        
                        if ( pt.m_wCurrFrame >= _MAX_FLYTAILFRAME )
                        {
                            pt.m_wCurrFrame = 0;
                        }
                    }

                    float fLife = cast(float)(cast(float)pt.m_wCurrLife / cast(float)pt.m_wLife);

                    bRate = cast(ubyte)(pt.m_bFstRed - pt.m_bFstRed * fLife);
                    pt.m_bRed   = bRate;
                    bRate = cast(ubyte)(pt.m_bFstGreen - pt.m_bFstGreen * fLife);
                    pt.m_bGreen = bRate;
                    bRate = cast(ubyte)(pt.m_bFstBlue - pt.m_bFstBlue * fLife);
                    pt.m_bBlue  = bRate;
                    
                    UpdateAirFiction(index);
                    UpdateMove(index);
                }
                
                nPartCnt++;
                if ( nPartCnt >= m_shPartNum )
                    return;
            }
        }
    }
    
    public override void SetParticleDefault(int index, FloatPoint vecGenPos)
    {
        auto pt = &m_pxParticle[index];
        
        *pt = Particle.zero;
        pt.m_vecPos     = vecGenPos;
        pt.m_vecVel     = FloatPoint(uniform(-50, 50), uniform(-30, 60));
        pt.m_wLife      = cast(ushort)uniform(150, 400);
        pt.m_fMass      = 1000.0f;
        pt.m_fSize      = uniform(0.5f, 4.0f) + RandomFloat();
        
        pt.m_bRed       = pt.m_bFstRed   = cast(ubyte)uniform(200, 255);
        pt.m_bGreen     = pt.m_bFstGreen = pt.m_bFstRed / 2;
        pt.m_bBlue      = pt.m_bFstBlue  = cast(ubyte)uniform(0, 30);
        pt.m_wDelay     = cast(ushort)uniform(200, 300);
        
        pt.m_bIsDead    = false;
    }
}



const _MAX_SMOKEFRAME = 1;

public class Smoke : ParticleSystem
{ 
    private ALLEGRO_BITMAP*[_MAX_SMOKEFRAME] m_pszSmokeFile;
    private int m_shPartNum;
    
    
    public this()
    {
        InitSystem();
    }
    
    public ~this()
    {
        DestroySystem();
    }
    
    public override void LoadTextr()
    {
        static immutable string[] szFileList = 
        [
            "smoke0.bmp"
        ];
        for ( int nCnt = 0; nCnt < _MAX_SMOKEFRAME; nCnt++ )
        {
            m_pszSmokeFile[nCnt] = al_load_bitmap(szFileList[nCnt].toStringz);
        }
    }
    
    public override void InitSystem()
    {
        super.InitSystem();
        m_shPartNum = 0;
        m_pszSmokeFile[] = null;
    }
    
    public override void DestroySystem()
    {
        super.DestroySystem();
        
        for ( int nCnt = 0; nCnt < _MAX_SMOKEFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_pszSmokeFile[nCnt]);
        }
        InitSystem();
    }
    
    public override void SetupSystem(int count = 100)
    {
        InitSystem();
        super.SetupSystem(count);
        
        LoadTextr();
        
        SetEnvFactor(-0.05f, FloatPoint(0, 1000));
    }
    
    public override bool RenderSystem()
    {
        int nPartCnt = 0;
        int op, source, dest;
        al_get_blender(&op, &source, &dest);
        al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_INVERSE_ALPHA);
        
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
                continue;
            
            immutable tint_color = al_map_rgba(pt.m_bRed, pt.m_bGreen, pt.m_bBlue, 0);
            
            al_draw_tinted_scaled_rotated_bitmap(m_pszSmokeFile[pt.m_wCurrFrame],
                                                 tint_color, 0, 0, pt.m_vecPos.getX(), pt.m_vecPos.getY(), pt.m_fSize, pt.m_fSize,
                                                 0, 0);
            nPartCnt++;
            if ( nPartCnt >= m_shPartNum )
                break;
        }
        
        al_set_blender(op, source, dest);
        
        return true;
    }
    
    public void SetSmokeParticle(FloatPoint vecGenPos)
    {
        int nGenCnt = 0;
        foreach (int index; 0..m_wNum)
        {
            immutable pt = m_pxParticle[index];
            
            if ( pt.m_bIsDead )
            {
                SetParticleDefault(index, vecGenPos);
                m_shPartNum++;
                
                nGenCnt++;
                
                if ( nGenCnt > 0 )
                    return;
            }
        }
    }
    
    public override void UpdateSystem(int nLoopTime, FloatPoint vecGenPos = FloatPoint.zero)
    {
        ubyte bRate;
        int nSpeedRate = (nLoopTime / 17);
        int nPartCnt = 0;
        FloatPoint vecAddVel;
        
        if ( nSpeedRate < 1 )       
            nSpeedRate = 1;
        
        m_fDeltaTime = 0.02f*nSpeedRate;
        
        foreach (int index; 0..m_wNum)
        {
            auto pt = &m_pxParticle[index];
            
            if ( !pt.m_bIsDead )
            {
                pt.m_wCurrLife += nLoopTime;
                
                if ( pt.m_wCurrLife > pt.m_wLife )
                {
                    *pt = Particle.zero;
                    m_shPartNum--;
                    nPartCnt--;
                }
                else
                {
                    pt.m_wCurrDelay += nLoopTime;
                    
                    if ( pt.m_wCurrDelay > pt.m_wDelay )
                    {
                        pt.m_wCurrDelay = 0;
                        pt.m_wCurrFrame++;
                        
                        if ( pt.m_wCurrFrame >= _MAX_SMOKEFRAME )
                        {
                            pt.m_wCurrFrame = 0;
                        }
                    }
                    
                    float fLife = cast(float)(cast(float)pt.m_wCurrLife / cast(float)pt.m_wLife);
                    float xx = uniform(-1, 1);
                    pt.m_fSize = (pt.m_fOriSize + pt.m_fOriSize * 7.0f * fLife) / 100;
                    vecAddVel = FloatPoint(xx, 0.0f);
                    //pt.m_vecVel += vecAddVel;
                    pt.m_vecVel.setX(pt.m_vecVel.getX() + xx);
                    
                    bRate = cast(ubyte)(pt.m_bFstRed - pt.m_bFstRed * fLife);
                    pt.m_bRed   = bRate;
                    bRate = cast(ubyte)(pt.m_bFstGreen - pt.m_bFstGreen * fLife);
                    pt.m_bGreen = bRate;
                    bRate = cast(ubyte)(pt.m_bFstBlue - pt.m_bFstBlue * fLife);
                    pt.m_bBlue  = bRate;
                    
                    UpdateAirFiction(index);
                    UpdateMove(index);
                }
                
                nPartCnt++;
                if ( nPartCnt >= m_shPartNum )
                    return;
            }
        }
    }
    
    public override void SetParticleDefault(int index, FloatPoint vecGenPos)
    {
        auto pt = &m_pxParticle[index];
        
        *pt = Particle.zero;
        pt.m_vecPos     = vecGenPos;
        pt.m_vecVel     = FloatPoint(uniform(-8, 8), 0);
        pt.m_wLife      = cast(ushort)uniform(150, 400);
        pt.m_fMass      = 1000.0f;
        pt.m_fSize      = pt.m_fOriSize = uniform(0.5f, 1.0f) + RandomFloat();


        ubyte color     = cast(ubyte)uniform(100, 150);
        pt.m_bRed       = pt.m_bFstRed   = color;
        pt.m_bGreen     = pt.m_bFstGreen = color;
        pt.m_bBlue      = pt.m_bFstBlue  = color;
        pt.m_wDelay     = cast(ushort)uniform(200, 300);
        
        pt.m_bIsDead    = false;
    }
}



const _MAX_ELECFRAME = 1;
const _MAX_ELEC_NODEX = 2;
const _MAX_ELEC_NODEY = 10;
const _ELEC_INDICES = _MAX_ELEC_NODEX * _MAX_ELEC_NODEY * 3 * 2;
const _ELEC_VERTICES = (_MAX_ELEC_NODEX + 1) * (_MAX_ELEC_NODEY + 1);

public class Electric
{
    private ALLEGRO_BITMAP*[_MAX_ELECFRAME] m_szElecFile;

    private int m_shPosX, m_shPosY;
    private int m_shDstPosX, m_shDstPosY;

    private float m_fSize;
    private float m_fRadian;

    private ALLEGRO_VERTEX m_avElec[_MAX_ELEC_NODEY+1][_MAX_ELEC_NODEX+1];
    private ALLEGRO_VERTEX m_avSaveElec[_MAX_ELEC_NODEY+1][_MAX_ELEC_NODEX+1];
    //private ALLEGRO_VERTEX m_avElec[_MAX_ELEC_NODE+1][2];
    private int m_pwElecIndices[_ELEC_INDICES];


    public this()
    {
        InitElec();
    }

    public ~this()
    {
        DestroyElec();
    }

    private void LoadTextr()
    {
        static immutable string[] szFileList = 
        [
            "elec0.bmp"
        ];
        for ( int nCnt = 0; nCnt < _MAX_ELECFRAME; nCnt++ )
        {
            m_szElecFile[nCnt] = al_load_bitmap(szFileList[nCnt].toStringz);
        }
    }

    public void InitElec()
    {
        // TODO:
        m_szElecFile[] = null;
        m_pwElecIndices = m_pwElecIndices.init;
        m_avElec = m_avElec.init;
        m_avSaveElec = m_avSaveElec.init;
        //ZeroMemory(m_szElecFile, _MAX_SNOWFRAME*MAX_PATH);
        //ZeroMemory(m_pwElecIndices, _ELEC_INDICES*sizeof(WORD));
        //ZeroMemory(m_avElec, sizeof(D3DVERTEX)*_MAX_ELEC_NODEY+1*_MAX_ELEC_NODEX+1);
        //ZeroMemory(m_avSaveElec, (_MAX_ELEC_NODEY+1)*(_MAX_ELEC_NODEX+1)*sizeof(D3DVERTEX));
        
        m_fSize     = 0;
        m_fRadian   = 0;
        m_shPosX    = 0;
        m_shPosY    = 0;
        m_shDstPosX = 0;
        m_shDstPosY = 0;
    }

    public void DestroyElec()
    {
        for ( int nCnt = 0; nCnt < _MAX_ELECFRAME; nCnt++ )
        {
            al_destroy_bitmap(m_szElecFile[nCnt]);
        }
        
        InitElec();
    }

    public void SetupElec()
    {
        LoadTextr();

        int bmp_width = al_get_bitmap_width(m_szElecFile[0]);
        int bmp_height = al_get_bitmap_height(m_szElecFile[0]);

        int nCntX, nCntY;

        immutable color = al_map_rgba(200, 200, 255, 0);
        
        for ( nCntY = 0; nCntY < (_MAX_ELEC_NODEY+1); nCntY++ )
        {
            for ( nCntX = 0; nCntX < (_MAX_ELEC_NODEX+1); nCntX++ )
            {
                float fXPos = cast(float)nCntX / cast(float)_MAX_ELEC_NODEX;
                float fYPos = cast(float)nCntY / cast(float)_MAX_ELEC_NODEY;
                //float fu = cast(float)nCntX / cast(float)_MAX_ELEC_NODEX;
                //float fv = cast(float)nCntY / cast(float)_MAX_ELEC_NODEY;
                float fu = (fXPos / 1.0f) * bmp_width;
                float fv = (fYPos / 1.0f) * bmp_height;

                m_avElec[nCntY][nCntX] = ALLEGRO_VERTEX(fXPos, fYPos, 0.0f,
                                                        fu, fv,
                                                        color);

                //m_avElec[nCntY][nCntX] = ALLEGRO_VERTEX(fXPos, fYPos, RGB(0, 255, 0), RGB(255, 0, 0), fu, fv);
            }
        }

        m_avSaveElec = m_avElec;
        
        // Å¸ÀÏÆú¸®°ïÀÇ ÀÎµ¦½º »ý¼º.
        int* pwIndex = &m_pwElecIndices[0];
        for ( nCntY = 0; nCntY < _MAX_ELEC_NODEY; nCntY++ )
        {
            for ( nCntX = 0; nCntX < _MAX_ELEC_NODEX; nCntX++ )
            {
                *pwIndex++ = (nCntX+1)+((_MAX_ELEC_NODEX+1)*nCntY);
                *pwIndex++ = nCntX+((_MAX_ELEC_NODEX+1)*(nCntY+1));
                *pwIndex++ = (nCntX+1)+((_MAX_ELEC_NODEX+1)*(nCntY+1));
                *pwIndex++ = nCntX+((_MAX_ELEC_NODEX+1)*(nCntY+1));
                *pwIndex++ = (nCntX+1)+((_MAX_ELEC_NODEX+1)*nCntY);
                *pwIndex++ = (nCntX)+((_MAX_ELEC_NODEX+1)*nCntY);
            }
        }
    }

    public void UpdateElec(int shPosX, int shPosY, int shDstPosX, int shDstPosY)
    {
        float fDiv = 0.0f;
        
        m_shPosX = shPosX;
        m_shPosY = shPosY;
        m_shDstPosX = shDstPosX;
        m_shDstPosY = shDstPosY;
        
        int nAbsX = abs(m_shDstPosX - m_shPosX);
        int nAbsY = abs(m_shDstPosY - m_shPosY);
        
        m_fSize = sqrt(cast(float)(nAbsX*nAbsX + nAbsY*nAbsY));
        
        //fDiv = cast(float)(nAbsY)/cast(float)(nAbsX);
        
        fDiv = cast(float)-(m_shDstPosX - m_shPosX) / m_fSize;

        m_fRadian = cast(float)asin(fDiv);
        
        if ( m_shDstPosY - m_shPosY > 0 )
            m_fRadian = std.math.PI_2 + std.math.PI_2 - m_fRadian;
        
        static int nCnt = 0;
        
        nCnt++;
        
        if ( nCnt >= 3 )
            nCnt = 0;
        
        if ( nCnt == 2 )
        {
            for ( int nCntY = 1; nCntY < _MAX_ELEC_NODEY; nCntY++ )
            {
                //float fRandX = RandomFloat() * 1.3f - 1.0f;
                float fRandX = RandomFloat() * 0.8f - 0.4f;
                float fRandY = RandomFloat() * 0.2f;

                for ( int nCntX = 0; nCntX < (_MAX_ELEC_NODEX+1); nCntX++ )
                {
                    m_avElec[nCntY][nCntX].x = m_avSaveElec[nCntY][nCntX].x + fRandX;
                    //m_avElec[nCntY][nCntX].y = m_avSaveElec[nCntY][nCntX].y + fRandY;
                }
            }
        }
    }

    public bool RenderElec()
    {
        ALLEGRO_TRANSFORM identity, world;
        
        al_identity_transform(&identity);
        al_identity_transform(&world);

        //int op, source, dest;
        //al_get_blender(&op, &source, &dest);
        //al_set_blender(ALLEGRO_BLEND_OPERATIONS.ALLEGRO_ADD, ALLEGRO_BLEND_MODE.ALLEGRO_ONE, ALLEGRO_BLEND_MODE.ALLEGRO_INVERSE_ALPHA);

        immutable float angle = m_fRadian;
        const float scalex = 64.0f;
        const float scaley = 512.0f;

        //al_build_transform(&world, m_shPosX, m_shPosY, scalex, scaley, 0);
        al_scale_transform(&world, scalex, scaley);
        al_rotate_transform(&world, angle);
        al_translate_transform(&world, m_shPosX, m_shPosY);

        al_use_transform(&world);
        int prim = al_draw_indexed_prim(m_avElec.ptr, null, m_szElecFile[0], m_pwElecIndices.ptr, 
                                        _ELEC_INDICES, ALLEGRO_PRIM_TYPE.ALLEGRO_PRIM_TRIANGLE_LIST);
        al_use_transform(&identity);

        //al_set_blender(op, source, dest);

        return true;
    }
}